package xuecheng.tool.api.aop;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import xuecheng.tool.json.JsonUtils;
import xuecheng.tool.json.JsonUtilsFactory;

/**
 * <p>
 * 使用 aop 切面记录请求日志信息
 * </p>
 *
 * @author pengxuecheng
 * @version 1.0.0
 */
@Aspect
@Slf4j
public class RequestLogAop {

    private JsonUtils jsonUtils = JsonUtilsFactory.obtain();

    /**
     * 切入点
     */
    @Pointcut("@annotation(xuecheng.tool.api.aop.RequestLog)||@within(xuecheng.tool.api.aop.RequestLog)")
    public void pointcut() {}

    /**
     * 环绕操作
     *
     * @param point
     *            切入点
     * @return 原方法返回值
     * @throws Throwable
     *             异常信息
     */
    @Around("pointcut()")
    public Object main(ProceedingJoinPoint point) throws Throwable {
        Long start = System.currentTimeMillis();
        // 返回值
        Object result = point.proceed();
        Long end = System.currentTimeMillis();
        // 请求耗时毫秒
        String consumingTimeStr = StrUtil.format("{}ms", end - start);

        // 先获取 request 对象, 不能在 异步方法方法里面再进行获取
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();

        // 输出日志
        SpringUtil.getBean(this.getClass()).printLog(point, result, consumingTimeStr, request);

        return result;
    }

    /**
     * 输出日志
     *
     * @param point
     *            连接点
     * @param result
     *            请求结果
     * @param consumingTimeStr
     *            请求耗时
     * @param request
     *            请求对象
     */
    @Async
    public void printLog(ProceedingJoinPoint point, Object result, String consumingTimeStr,
        HttpServletRequest request) {
        // 请求 URL
        StringBuffer requestURL = request.getRequestURL();
        // 请求参数
        String queryString = request.getQueryString();
        // 请求 IP
        String remoteAddr = request.getRemoteAddr();
        // HTTP 请求类型
        String method = request.getMethod();

        // 原始 User-Agent
        String header = request.getHeader("User-Agent");
        UserAgent userAgent = UserAgent.parseUserAgentString(header);
        // 浏览器类型
        String browser = userAgent.getBrowser().toString();
        // 操作系统
        String operatingSystem = userAgent.getOperatingSystem().toString();

        // 请求方法签名
        String methodSignature = point.getSignature().toLongString();
        // 请求方法参数
        List<String> args = Arrays.stream(Optional.ofNullable(point.getArgs()).orElse(new Object[0]))
            .map(o -> jsonUtils.toJson(o)).collect(Collectors.toList());
        String resultJson = "void";
        if (result != null) {
            resultJson = jsonUtils.toJson(result);
        }

        log.info("|HTTP请求日志|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", consumingTimeStr, requestURL, queryString, remoteAddr,
            method, userAgent, browser, operatingSystem, methodSignature, resultJson, args);
    }

    // /**
    // * 前置操作
    // *
    // * @param point
    // * 切入点
    // */
    // @Before("log()")
    // public void beforeLog(JoinPoint point) {
    // ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
    //
    // HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
    //
    // log.info("【请求 URL】：{}", request.getRequestURL());
    // log.info("【请求 IP】：{}", request.getRemoteAddr());
    // log.info("【请求类名】：{}，【请求方法名】：{}", point.getSignature().getDeclaringTypeName(),
    // point.getSignature().getName());
    //
    // Map<String, String[]> parameterMap = request.getParameterMap();
    // log.info("【请求参数】：{}，", JSONUtil.toJsonStr(parameterMap));
    // Long start = System.currentTimeMillis();
    // request.setAttribute(START_TIME, start);
    // }

    // /**
    // * 后置操作
    // */
    // @AfterReturning("log()")
    // public void afterReturning() {
    // ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
    // HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
    //
    // Long start = (Long)request.getAttribute(START_TIME);
    // Long end = System.currentTimeMillis();
    // log.info("【请求耗时】：{}毫秒", end - start);
    //
    // String header = request.getHeader("User-Agent");
    // UserAgent userAgent = UserAgent.parseUserAgentString(header);
    // log.info("【浏览器类型】：{}，【操作系统】：{}，【原始User-Agent】：{}", userAgent.getBrowser().toString(),
    // userAgent.getOperatingSystem().toString(), header);
    // }
}